package com.redhipps.hips.client.controller;

import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FocusListenerAdapter;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.KeyboardListenerAdapter;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.MouseListenerAdapter;
import com.google.gwt.user.client.ui.SourcesMouseEvents;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;

public class InlineEditController {

  public InlineEditController() {}

  public <CompletionType> Widget wrap(Widget source,
      AutoCompleteController<CompletionType> autoCompleteController,
      CompletionListener<CompletionType> listener) {
    FlowPanel container = wrapInContainerPanel(source);
    Label label = (Label) source;
    ((SourcesMouseEvents) source).addMouseListener(
        new AutoCompleteMouseListener<CompletionType>(
            new AutoCompleteHideEditBoxListener(label, autoCompleteController),
            autoCompleteController, listener, label));
    
    return container;
  }

  public Widget wrap(Widget source, EditCallback<String> callback) {
    FlowPanel container = wrapInContainerPanel(source);
    ((SourcesMouseEvents) source).addMouseListener(
        new DefaultMouseListener(
            new InlineEditKeyListener(),
            new DefaultHideEditBoxListener((Label) source, callback)));
    return container;
  }

  private FlowPanel wrapInContainerPanel(Widget source) {
    FlowPanel container = new FlowPanel();
    container.add(source);
    return container;
  }

  private static void swap(Widget incoming, Widget outgoing) {
    HasWidgets parent = (HasWidgets) outgoing.getParent();
    parent.remove(outgoing);
    incoming.setVisible(true);
  }

  private abstract class InlineEditMouseListener extends MouseListenerAdapter {

    @Override
    public void onMouseUp(Widget sender, int x, int y) {
      Label label = (Label) sender;
      TextBox editBox = new TextBox();
      editBox.setText(label.getText());
      Widget wrappedTextBox = wrapTextBox(editBox);
      label.setVisible(false);
      FlowPanel parent = (FlowPanel) sender.getParent();
      for (int i = 0; i < parent.getWidgetCount(); i++) {
        if (parent.getWidget(i) == sender) {
          parent.insert(wrappedTextBox, i);
          editBox.setFocus(true);
          editBox.selectAll();
          break;
        }
      }
    }

    abstract protected Widget wrapTextBox(TextBox editBox);
  }

  private class DefaultMouseListener extends InlineEditMouseListener {

    private KeyboardListener keyListener;
    private HideEditBoxListener focusListener;

    public DefaultMouseListener(KeyboardListener keyListener, HideEditBoxListener focusListener) {
      this.keyListener = keyListener;
      this.focusListener = focusListener;
    }

    @Override
    protected Widget wrapTextBox(TextBox editBox) {
      editBox.addKeyboardListener(keyListener);
      editBox.addFocusListener(focusListener);
      return editBox;
    }

  }

  private class AutoCompleteMouseListener<CompletionType> extends InlineEditMouseListener {
    
    private AutoCompleteController<CompletionType> autoCompleteController;
    private CompletionListener<CompletionType> listener;
    private HideEditBoxListener hideEditBoxListener;
    private Label staticDisplayLabel;

    public AutoCompleteMouseListener(
        HideEditBoxListener hideEditBoxListener,
        AutoCompleteController<CompletionType> autoCompleteController,
        CompletionListener<CompletionType> listener,
        Label label) {
      this.hideEditBoxListener = hideEditBoxListener;
      this.autoCompleteController = autoCompleteController;
      this.listener = listener;
      this.staticDisplayLabel = label;
    }

    @Override
    protected Widget wrapTextBox(final TextBox editBox) {
      CompletionListener<CompletionType> completionListener =
          new CompletionListener<CompletionType>() {
        public void valueCompleted(String formatted, CompletionType value) {
          staticDisplayLabel.setText(formatted);
          swap(staticDisplayLabel, editBox);
          listener.valueCompleted(formatted, value);
        }
      };
      editBox.addFocusListener(hideEditBoxListener);
      return autoCompleteController.wrap(editBox, completionListener);
    }
  }

  private class InlineEditKeyListener extends KeyboardListenerAdapter {

    @Override
    public void onKeyUp(Widget sender, char keyCode, int modifiers) {
      switch (keyCode) {
        case KEY_ENTER:
          // This will trigger the focus listener to save the modified text.
          // TODO Find a more elegant way to do this.
          ((FocusWidget) sender).setFocus(false);
          break;
        case KEY_ESCAPE:
          // TODO Revert the edit
          break;
      }
    }

  }

  private abstract class HideEditBoxListener extends FocusListenerAdapter {

    private Label staticDisplayLabel;
    
    public HideEditBoxListener(Label staticDisplayLabel) {
      this.staticDisplayLabel = staticDisplayLabel;
    }

    @Override
    public void onLostFocus(Widget sender) {
      staticDisplayLabel.setText(((TextBox) sender).getText());
      swap(staticDisplayLabel, sender);
    }
  }

  private class DefaultHideEditBoxListener extends HideEditBoxListener {

    private EditCallback<String> editCallback;
    
    public DefaultHideEditBoxListener(Label staticDisplayLabel,
        EditCallback<String> editCallback) {
      super(staticDisplayLabel);
      this.editCallback = editCallback;
    }

    @Override
    public void onLostFocus(Widget sender) {
      super.onLostFocus(sender);
      TextBox textBox = (TextBox) sender;
      editCallback.onSuccess(textBox.getText());
    }
  }

  private class AutoCompleteHideEditBoxListener extends HideEditBoxListener {

    private AutoCompleteController<?> autoCompleteController;

    public AutoCompleteHideEditBoxListener(Label label, AutoCompleteController<?> c) {
      super(label);
      this.autoCompleteController = c;
    }

    @Override
    public void onLostFocus(Widget sender) {
      //if (!autoCompleteController.isVisible()) {
        super.onLostFocus(sender);
      //}
    }
  }
}
